package com.util.concurrent.lock;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-07-25 16:15:16
 */
public class Cache<T> {
    private static final Logger log = LoggerFactory.getLogger(Cache.class);
    private static final StampedLock lock = new StampedLock();
    private static final ObjectMapper mapper = new ObjectMapper();
    private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

    public void write(String key, T o) {
        long stamped = lock.writeLock();
        try {
            String s = mapper.writeValueAsString(o);
            map.put(key, s);
            log.info("write[k:{},v:{}]", key, s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlockWrite(stamped);
        }
    }

    public T read(String key, Class<T> clazz) {
        long stamped = lock.tryOptimisticRead();
        T o;
        try {
            o = mapper.readValue(map.get(key), clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        if (!lock.validate(stamped)) {
            stamped = lock.readLock();
            try {
                o = mapper.readValue(map.get(key), clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlockRead(stamped);
            }
        }
        log.info("read[k:{},v:{}]", key, o);
        return o;
    }

    public void modify(String key, T t) {
        long stamped = lock.readLock();
        try {
            long wl = lock.tryConvertToWriteLock(stamped);
            while (true) {
                if (wl != 0L) {
                    String s = mapper.writeValueAsString(t);
                    map.put(key, s);
                    break;
                } else {
                    lock.unlockRead(stamped);
                    stamped = lock.writeLock();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock(stamped);
        }
    }

}
